home *** CD-ROM | disk | FTP | other *** search
/ PsL Monthly 1993 December / PSL Monthly Shareware CD-ROM (December 1993).iso / prgmming / win / pascal / cdfile.exe / CD_UNIT.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1992-11-10  |  14.4 KB  |  726 lines

  1. Unit CD_Unit;
  2.  
  3. Interface
  4.  
  5. Uses DOS, CD_Vars;
  6.  
  7. Var
  8.   Drive   : Integer;  { Must set drive before all operations }
  9.   SubUnit : Integer;
  10.  
  11. function File_Name(var Code : Integer) : String;
  12.  
  13. function Read_VTOC(var VTOC : VTOCArray;
  14.                    var Index : Integer) : Boolean;
  15.  
  16. procedure CD_Check(var Code : Integer);
  17.  
  18. procedure Vol_Desc(Var Code : Integer;
  19.                    var ErrCode : Integer);
  20.  
  21. procedure CD_Dev_Req(DevPointer : Pointer);
  22.  
  23. procedure Get_Dir_Entry(PathName : String;
  24.                         var Format, ErrCode : Integer);
  25.  
  26. procedure DeviceStatus;
  27.  
  28. procedure Audio_Channel_Info;
  29.  
  30. procedure Audio_Disk_Info;
  31.  
  32. procedure Audio_Track_Info(Var StartPoint : LongInt;
  33.                            Var TrackControl : Byte);
  34.  
  35. procedure Audio_Status_Info;
  36.  
  37. procedure Q_Channel_Info;
  38.  
  39. procedure Lock(LockDrive : Boolean);
  40.  
  41. procedure Reset;
  42.  
  43. procedure Eject;
  44.  
  45. procedure CloseTray;
  46.  
  47. procedure Resume_Play;
  48.  
  49. procedure Pause_Audio;
  50.  
  51. procedure Play_Audio(StartSec, EndSec : LongInt);
  52.  
  53. function Sector_Size(ReadMode : Integer) : Word;
  54.  
  55. function Volume_Size : LongInt;
  56.  
  57. function Media_Changed : Boolean;
  58.  
  59. function Head_Location(AddrMode : Byte) : LongInt;
  60.  
  61. procedure Read_Drive_Bytes(Var ReadBytes : DriveByteArray);
  62.  
  63. procedure Read_Long(TransAddr : Pointer; StartSec : Longint);
  64.  
  65. procedure SeekSec(StartSec : Longint);
  66.  
  67. procedure DevClose;
  68.  
  69. procedure DevOpen;
  70.  
  71. procedure OutputFlush;
  72.  
  73. procedure InputFlush;
  74.  
  75. function UPC_Code : String;
  76.  
  77. Implementation
  78.  
  79. Const
  80.   CarryFlag  = $0001;
  81.  
  82. Type
  83.   PointerHalf = Record
  84.      LoHalf, HiHalf : Word;
  85.   End;
  86.  
  87. Var
  88.   Regs       : Registers;
  89.   IOBlock    : IOControl;
  90.   DriveBytes : Array[1..130] of Byte;
  91.  
  92. procedure Clear_Regs;
  93. begin
  94.   FillChar(Regs, SizeOf(Regs), #0);
  95. end;
  96.  
  97. procedure CD_Intr;
  98. begin
  99.   Regs.AH := $15;
  100.   Intr($2F, Regs);
  101. end;
  102.  
  103. procedure MSCDEX_Ver;
  104. begin
  105.   Clear_Regs;
  106.   Regs.AL := $0C;
  107.   Regs.BX := $0000;
  108.   CD_Intr;
  109.   MSCDEX_Version.Minor := 0;
  110.   If Regs.BX = 0 Then
  111.      MSCDEX_Version.Major := 1
  112.   ELSE
  113.      Begin
  114.        MSCDEX_Version.Major := Regs.BH;
  115.        MSCDEX_Version.Minor := Regs.BL;
  116.      End;
  117. end;
  118.  
  119. procedure Initialize;
  120. begin
  121.   NumberOfCD := 0;
  122.   Clear_Regs;
  123.   Regs.AL := $00;
  124.   Regs.BX := $0000;
  125.   CD_Intr;
  126.   If Regs.BX <> 0 THEN
  127.      Begin
  128.        NumberOfCD := Regs.BX;
  129.        FirstCD := Regs.CX;
  130.        Clear_Regs;
  131.        FillChar(DriverList, SizeOf(DriverList), #0);
  132.        FillChar(UnitList, SizeOf(UnitList), #0);
  133.        Regs.AL := $01;               { Get List of Driver Header Addresses }
  134.        Regs.ES := Seg(DriverList);
  135.        Regs.BX := Ofs(DriverList);
  136.        CD_Intr;
  137.        Clear_Regs;
  138.        Regs.AL := $0D;               { Get List of CD-ROM Units }
  139.        Regs.ES := Seg(UnitList);
  140.        Regs.BX := Ofs(UnitList);
  141.        CD_Intr;
  142.        MSCDEX_Ver;
  143.      End;
  144. end;
  145.  
  146.  
  147. function File_Name(var Code : Integer) : String;
  148. Var
  149.   FN : String[38];
  150. begin
  151.   Clear_Regs;
  152.   Regs.AL := Code + 1;
  153. {
  154.        Copyright Filename     =  1
  155.        Abstract Filename      =  2
  156.        Bibliographic Filename =  3
  157. }
  158.   Regs.CX := Drive;
  159.   Regs.ES := Seg(FN);
  160.   Regs.BX := Ofs(FN);
  161.   CD_Intr;
  162.   Code := Regs.AX;
  163.   If (Regs.Flags AND CarryFlag) = 0 THEN
  164.      File_Name := FN
  165.   ELSE
  166.      File_Name := '';
  167. end;
  168.  
  169.  
  170. function Read_VTOC(var VTOC : VTOCArray;
  171.                    var Index : Integer) : Boolean;
  172. { On entry -
  173.      Index = Vol Desc Number to read from 0 to ?
  174.   On return
  175.      Case Index of
  176.             1    : Standard Volume Descriptor
  177.             $FF  : Volume Descriptor Terminator
  178.             0    : All others
  179. }
  180. begin
  181.   Clear_Regs;
  182.   Regs.AL := $05;
  183.   Regs.CX := Drive;
  184.   Regs.DX := Index;
  185.   Regs.ES := Seg(VTOC);
  186.   Regs.BX := Ofs(VTOC);
  187.   CD_Intr;
  188.   Index := Regs.AX;
  189.   If (Regs.Flags AND CarryFlag) = 0 THEN
  190.      Read_VTOC := TRUE
  191.   ELSE
  192.      Read_VTOC := FALSE;
  193. end;
  194.  
  195. procedure CD_Check(var Code : Integer);
  196. begin
  197.   Clear_Regs;
  198.   Regs.AL := $0B;
  199.   Regs.BX := $0000;
  200.   Regs.CX := Drive;
  201.   CD_Intr;
  202.   If Regs.BX <> $ADAD THEN
  203.      Code := 2
  204.   ELSE
  205.      Begin
  206.        If Regs.AX <> 0 THEN
  207.           Code := 0
  208.        ELSE
  209.           Code := 1;
  210.      End;
  211. end;
  212.  
  213.  
  214. procedure Vol_Desc(Var Code : Integer;
  215.                    var ErrCode : Integer);
  216.  
  217.   function Get_Vol_Desc : Byte;
  218.     begin
  219.       Clear_Regs;
  220.       Regs.CX := Drive;
  221.       Regs.AL := $0E;
  222.       Regs.BX := $0000;
  223.       CD_Intr;
  224.       Code := Regs.AX;
  225.       If (Regs.Flags AND CarryFlag) <> 0 THEN
  226.          ErrCode := $FF;
  227.       Get_Vol_Desc := Regs.DH;
  228.     end;
  229.  
  230. begin
  231.   Clear_Regs;
  232.   ErrCode := 0;
  233.   If Code <> 0 THEN
  234.      Begin
  235.        Regs.DH := Code;
  236.        Regs.DL := 0;
  237.        Regs.BX := $0001;
  238.        Regs.AL := $0E;
  239.        Regs.CX := Drive;
  240.        CD_Intr;
  241.        Code := Regs.AX;
  242.        If (Regs.Flags AND CarryFlag) <> 0 THEN
  243.           ErrCode := $FF;
  244.      End;
  245.   If ErrCode = 0 THEN
  246.      Code := Get_Vol_Desc;
  247. end;
  248.  
  249. procedure Get_Dir_Entry(PathName : String;
  250.                         var Format, ErrCode : Integer);
  251. begin
  252.   FillChar(DirBuf, SizeOf(DirBuf), #0);
  253.   PathName := PathName + #0;
  254.   Clear_Regs;
  255.   Regs.AL := $0F;
  256.   Regs.CL := Drive;
  257.   Regs.CH := 1;
  258.   Regs.ES := Seg(PathName);
  259.   Regs.BX := Ofs(PathName);
  260.   Regs.SI := Seg(DirBuf);
  261.   Regs.DI := Ofs(DirBuf);
  262.   CD_Intr;
  263.   ErrCode := Regs.AX;
  264.   If (Regs.Flags AND CarryFlag) = 0 THEN
  265.      Begin
  266.        Move(DirBuf.NameArray[1], DirBuf.FileName[1], 38);
  267.        DirBuf.FileName[0] := #12; { File names are only 8.3 }
  268.        Format := Regs.AX
  269.      End
  270.   ELSE
  271.      Format := $FF;
  272. end;
  273.  
  274. procedure CD_Dev_Req(DevPointer : Pointer);
  275. begin
  276.   Clear_Regs;
  277.   Regs.AL := $10;
  278.   Regs.CX := Drive;
  279.   Regs.ES := PointerHalf(DevPointer).HiHalf;
  280.   Regs.BX := PointerHalf(DevPointer).LoHalf;
  281.   CD_Intr;
  282. end;
  283.  
  284. procedure IO_Control(Command : Byte);
  285. begin
  286.   IOBlock.IOReq_Hdr.Len := 26;
  287.   IOBlock.IOReq_Hdr.SubUnit := SubUnit;
  288.   IOBlock.IOReq_Hdr.Status := 0;
  289.   IOBlock.TransAddr := @DriveBytes;
  290.   IOBlock.IOReq_Hdr.Command := Command;
  291.  
  292.   FillChar(IOBlock.IOReq_Hdr.Reserved, 8, #0);
  293.  
  294.   CD_Dev_Req(@IOBlock);
  295.  
  296.   Busy :=   (IOBlock.IOReq_Hdr.Status AND 512) <> 0;
  297.  
  298.  
  299. end;
  300.  
  301. procedure Audio_Channel_Info;
  302. begin
  303.   FillChar(DriveBytes, SizeOf(DriveBytes), #0);
  304.   DriveBytes[1] := 4;
  305.   IOBlock.NumBytes := 9;
  306.  
  307.   IO_Control(IOCtlInput);
  308.  
  309.   Move(DriveBytes, AudioChannel, 9);
  310. End;
  311.  
  312. procedure DeviceStatus;
  313. begin
  314.   FillChar(DriveBytes, SizeOf(DriveBytes), #0);
  315.  
  316.   DriveBytes[1] := 6;
  317.   IOBlock.NumBytes := 5;
  318.  
  319.   IO_Control(IOCtlInput);
  320.   DoorOpen     := DriveBytes[2] AND 1 <> 0;
  321.   DoorLocked   := DriveBytes[2] AND 2 <> 0;
  322.   Audio        := DriveBytes[2] AND 16 <> 0;
  323.   AudioManip   := DriveBytes[3] AND 1 <> 0;
  324.   DiscInDrive  := DriveBytes[3] AND 8 <> 0;
  325.   RedBook      := DriveBytes[3] AND 16 <> 0;
  326.  
  327.  
  328. End;
  329.  
  330. procedure Audio_Disk_Info;
  331. begin
  332.   FillChar(DriveBytes, SizeOf(DriveBytes), #0);
  333.  
  334.   DriveBytes[1] := 10;
  335.   IOBlock.NumBytes := 7;
  336.  
  337.   IO_Control(IOCtlInput);
  338.  
  339.   Move(DriveBytes[2], AudioDiskInfo, 6);
  340.  
  341.   Playing := Busy;
  342.  
  343. end;
  344.  
  345. procedure Audio_Track_Info(Var StartPoint : LongInt;
  346.                            Var TrackControl : Byte);
  347. begin
  348.   FillChar(DriveBytes, SizeOf(DriveBytes), #0);
  349.  
  350.   DriveBytes[1] := 11;
  351.   DriveBytes[2] := TrackControl;   { Track number }
  352.   IOBlock.NumBytes := 7;
  353.  
  354.   IO_Control(IOCtlInput);
  355.  
  356.   Move(DriveBytes[3], StartPoint, 4);
  357.  
  358.   TrackControl := DriveBytes[7];
  359.  
  360.   Playing := Busy;
  361. end;
  362.  
  363. procedure Q_Channel_Info;
  364. begin
  365.   FillChar(DriveBytes, SizeOf(DriveBytes), #0);
  366.  
  367.   DriveBytes[1] := 12;
  368.   IOBlock.NumBytes := 11;
  369.  
  370.   IO_Control(IOCtlInput);
  371.  
  372.   Move(DriveBytes[2], QChannelInfo, 11);
  373.  
  374. end;
  375.  
  376. procedure Audio_Status_Info;
  377. begin
  378.   FillChar(DriveBytes, SizeOf(DriveBytes), #0);
  379.  
  380.   DriveBytes[1] := 15;
  381.   IOBlock.NumBytes := 11;
  382.  
  383.   IO_Control(IOCtlInput);
  384.  
  385.   Paused := (Word(DriveBytes[2]) AND 1) <> 0;
  386.  
  387.   Move(DriveBytes[4], Last_Start, 4);
  388.   Move(DriveBytes[8], Last_End, 4);
  389.  
  390.   Playing := Busy;
  391. end;
  392.  
  393. procedure Eject;
  394. begin
  395.   FillChar(DriveBytes, SizeOf(DriveBytes), #0);
  396.  
  397.   DriveBytes[1] := 0;
  398.   IOBlock.NumBytes := 1;
  399.  
  400.   IO_Control(IOCtlOutput);
  401. end;
  402.  
  403. procedure Reset;
  404. begin
  405.   FillChar(DriveBytes, SizeOf(DriveBytes), #0);
  406.  
  407.   DriveBytes[1] := 2;
  408.   IOBlock.NumBytes := 1;
  409.  
  410.   IO_Control(IOCtlOutput);
  411.   Busy := TRUE;
  412. end;
  413.  
  414. procedure Lock(LockDrive : Boolean);
  415. begin
  416.   FillChar(DriveBytes, SizeOf(DriveBytes), #0);
  417.  
  418.   DriveBytes[1] := 1;
  419.   If LockDrive THEN
  420.      DriveBytes[2] := 1
  421.   ELSE
  422.      DriveBytes[2] := 0;
  423.   IOBlock.NumBytes := 2;
  424.  
  425.   IO_Control(IOCtlOutput);
  426. end;
  427.  
  428. procedure CloseTray;
  429. begin
  430.   FillChar(DriveBytes, SizeOf(DriveBytes), #0);
  431.  
  432.   DriveBytes[1] := 5;
  433.   IOBlock.NumBytes := 1;
  434.  
  435.   IO_Control(IOCtlOutput);
  436. end;
  437.  
  438. Var
  439.   AudioPlay : Audio_Play;
  440.  
  441.  
  442. function Play(StartLoc, NumSec : LongInt) : Boolean;
  443. begin
  444.   FillChar(AudioPlay, SizeOf(AudioPlay), #0);
  445.   AudioPlay.APReq.Command := PlayCD;
  446.   AudioPlay.APReq.Len := 22;
  447.   AudioPlay.APReq.SubUnit := SubUnit;
  448.   AudioPlay.Start := StartLoc;
  449.   AudioPlay.NumSecs := NumSec;
  450.   AudioPlay.AddrMode := 1;
  451.  
  452.   CD_Dev_Req(@AudioPlay);
  453.   Play := ((AudioPlay.APReq.Status AND 32768) = 0);
  454.  
  455. end;
  456.  
  457. procedure Play_Audio(StartSec, EndSec : LongInt);
  458. Var
  459.   SP,
  460.   EP     : LongInt;
  461.   SArray : Array[1..4] Of Byte;
  462.   EArray : Array[1..4] Of Byte;
  463. begin
  464.   Move(StartSec, SArray[1], 4);
  465.   Move(EndSec, EArray[1], 4);
  466.   SP := SArray[3];           { Must use longint or get negative result }
  467.   SP := (SP*75*60) + (SArray[2]*75) + SArray[1];
  468.   EP := EArray[3];
  469.   EP := (EP*75*60) + (EArray[2]*75) + EArray[1];
  470.   EP := EP-SP;
  471.  
  472.   Playing := Play(StartSec, EP);
  473.   Audio_Status_Info;
  474. end;
  475.  
  476. procedure Pause_Audio;
  477. begin
  478.   If Playing THEN
  479.      Begin
  480.        FillChar(AudioPlay, SizeOf(AudioPlay), #0);
  481.        AudioPlay.APReq.Command := StopPlay;
  482.        AudioPlay.APReq.Len := 13;
  483.        AudioPlay.APReq.SubUnit := SubUnit;
  484.        CD_Dev_Req(@AudioPlay);
  485.      end;
  486.   Audio_Status_Info;
  487.   Playing := FALSE;
  488. end;
  489.  
  490. procedure Resume_Play;
  491. begin
  492.   FillChar(AudioPlay, SizeOf(AudioPlay), #0);
  493.   AudioPlay.APReq.Command := ResumePlay;
  494.   AudioPlay.APReq.Len := 13;
  495.   AudioPlay.APReq.SubUnit := SubUnit;
  496.   CD_Dev_Req(@AudioPlay);
  497.   Audio_Status_Info;
  498. end;
  499.  
  500. function Sector_Size(ReadMode : Integer) : Word;
  501. Var SecSize : Word;
  502. begin
  503.   FillChar(DriveBytes, SizeOf(DriveBytes), #0);
  504.  
  505.   DriveBytes[1] := 7;
  506.   DriveBytes[2] := ReadMode;
  507.  
  508.   IOBlock.NumBytes := 4;
  509.  
  510.   IO_Control(IOCtlInput);
  511.  
  512.   Move(DriveBytes[3], SecSize, 2);
  513.   Sector_Size := SecSize;
  514. End;
  515.  
  516. function Volume_Size : LongInt;
  517. Var VolSize : LongInt;
  518. begin
  519.   FillChar(DriveBytes, SizeOf(DriveBytes), #0);
  520.  
  521.   DriveBytes[1] := 8;
  522.  
  523.   IOBlock.NumBytes := 5;
  524.  
  525.   IO_Control(IOCtlInput);
  526.  
  527.   Move(DriveBytes[2], VolSize, 4);
  528.   Volume_Size := VolSize;
  529. End;
  530.  
  531. function Media_Changed : Boolean;
  532. Var MedChng : Byte;
  533.  
  534. {  1  :  Media not changed
  535.    0  :  Don't Know
  536.   -1  :  Media changed
  537. }
  538. begin
  539.   FillChar(DriveBytes, SizeOf(DriveBytes), #0);
  540.  
  541.   DriveBytes[1] := 9;
  542.  
  543.   IOBlock.NumBytes := 2;
  544.  
  545.   IO_Control(IOCtlInput);
  546.  
  547.   Move(DriveBytes[2], MedChng, 4);
  548.   Inc(MedChng);
  549.   Case MedChng of
  550.        2    : Media_Changed := False;
  551.        1,0  : Media_Changed := True;
  552.   End;
  553. End;
  554.  
  555. function Head_Location(AddrMode : Byte) : LongInt;
  556. Var
  557.   HeadLoc : Longint;
  558. begin
  559.   FillChar(DriveBytes, SizeOf(DriveBytes), #0);
  560.  
  561.   DriveBytes[1] := 1;
  562.   DriveBytes[2] := AddrMode;
  563.  
  564.   IOBlock.NumBytes := 6;
  565.  
  566.   IO_Control(IOCtlInput);
  567.  
  568.   Move(DriveBytes[3], HeadLoc, 4);
  569.   Head_Location := HeadLoc;
  570. End;
  571.  
  572. procedure Read_Drive_Bytes(Var ReadBytes : DriveByteArray);
  573. Begin
  574.   FillChar(DriveBytes, SizeOf(DriveBytes), #0);
  575.  
  576.   DriveBytes[1] := 5;
  577.  
  578.   IOBlock.NumBytes := 130;
  579.  
  580.   IO_Control(IOCtlInput);
  581.  
  582.   Move(DriveBytes[3], ReadBytes, 128);
  583. End;
  584.  
  585. function UPC_Code : String;
  586. Var
  587.   I, J, K : Integer;
  588.   TempStr : String;
  589. Begin
  590.   FillChar(DriveBytes, SizeOf(DriveBytes), #0);
  591.   TempStr := '';
  592.   DriveBytes[1] := 14;
  593.  
  594.   IOBlock.NumBytes := 11;
  595.  
  596.   IO_Control(IOCtlInput);
  597.  
  598.   If ((IOBlock.IOReq_Hdr.Status AND 32768) = 0) THEN;
  599.      For I := 3 to 9 DO
  600.          Begin
  601.            J := DriveBytes[I] AND $0F;
  602.            K := DriveBytes[I] AND $F0;
  603.            TempStr := TempStr + Chr(J + 48);
  604.            TempStr := TempStr + Chr(K + 48);
  605.          End;
  606.   If Length(TempStr) > 13 THEN
  607.      TempStr[0] := Chr(Ord(TempStr[0])-1);
  608.   UPC_Code := TempStr;
  609. End;
  610.  
  611.  
  612.  
  613. procedure Read_Long(TransAddr : Pointer; StartSec : Longint);
  614. Var
  615.   RL : ReadControl;
  616. {
  617.   ReadControl = Record
  618.     IOReq_Hdr : Req_Hdr;
  619.     AddrMode  : Byte;
  620.     TransAddr : Pointer;
  621.     NumSecs   : Word;
  622.     StartSec  : LongInt;
  623.     ReadMode  : Byte;
  624.     IL_Size,
  625.     IL_Skip   : Byte;
  626.   End;
  627. }
  628. begin
  629.   FillChar(RL, SizeOf(RL), #0);
  630.   RL.IOReq_Hdr.Len := 27;
  631.   RL.IOReq_Hdr.SubUnit := SubUnit;
  632.   RL.IOReq_Hdr.Command := ReadLong;
  633.   RL.AddrMode := 1;
  634.   RL.TransAddr := TransAddr;
  635.   RL.NumSecs := 1;
  636.   RL.StartSec := StartSec;
  637.   RL.ReadMode := 0;
  638.   CD_Dev_Req(@RL);
  639. end;
  640.  
  641. procedure SeekSec(StartSec : Longint);
  642. Var
  643.   RL : ReadControl;
  644.  
  645. begin
  646.   FillChar(RL, SizeOf(RL), #0);
  647.   RL.IOReq_Hdr.Len := 24;
  648.   RL.IOReq_Hdr.SubUnit := SubUnit;
  649.   RL.IOReq_Hdr.Command := SeekCmd;
  650.   RL.AddrMode := 1;
  651.   RL.StartSec := StartSec;
  652.   RL.ReadMode := 0;
  653.   CD_Dev_Req(@RL);
  654. end;
  655.  
  656. procedure InputFlush;
  657. Var
  658.   IOReq : Req_Hdr;
  659. begin
  660.   FillChar(IOReq, SizeOf(IOReq), #0);
  661.   With IOReq DO
  662.   Begin
  663.      Len     := 13;
  664.      SubUnit := SubUnit;
  665.      Command := 7;
  666.      Status  := 0;
  667.   end;
  668.   CD_Dev_Req(@IOReq);
  669. end;
  670.  
  671. procedure OutputFlush;
  672. Var
  673.   IOReq : Req_Hdr;
  674. begin
  675.   FillChar(IOReq, SizeOf(IOReq), #0);
  676.   With IOReq DO
  677.   Begin
  678.      Len     := 13;
  679.      SubUnit := SubUnit;
  680.      Command := 11;
  681.      Status  := 0;
  682.   end;
  683.   CD_Dev_Req(@IOReq);
  684. end;
  685.  
  686. procedure DevOpen;
  687. Var
  688.   IOReq : Req_Hdr;
  689. begin
  690.   FillChar(IOReq, SizeOf(IOReq), #0);
  691.   With IOReq DO
  692.   Begin
  693.      Len     := 13;
  694.      SubUnit := SubUnit;
  695.      Command := 13;
  696.      Status  := 0;
  697.   end;
  698.   CD_Dev_Req(@IOReq);
  699. end;
  700.  
  701. procedure DevClose;
  702. Var
  703.   IOReq : Req_Hdr;
  704. begin
  705.   FillChar(IOReq, SizeOf(IOReq), #0);
  706.   With IOReq DO
  707.   Begin
  708.      Len     := 13;
  709.      SubUnit := SubUnit;
  710.      Command := 14;
  711.      Status  := 0;
  712.   end;
  713.   CD_Dev_Req(@IOReq);
  714. end;
  715.  
  716. {************************************************************}
  717.  
  718. Begin
  719.   NumberOfCD := 0;
  720.   FirstCD := 0;
  721.   FillChar(MSCDEX_Version, SizeOf(MSCDEX_Version), #0);
  722.   Initialize;
  723.   Drive := FirstCD;
  724.   SubUnit := 0;
  725. End.
  726.